package debtbundler.domain.entity.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import debtbundler.domain.entity.BalanceSheet;
import debtbundler.domain.entity.Debt;
import debtbundler.domain.entity.SharedCost;
import debtbundler.domain.entity.MoneyPool;
import debtbundler.domain.entity.Person;
import debtbundler.domain.entity.Signature;

class BalanceSheetImpl extends AbstractListenableEntity implements BalanceSheet {
	private static final long serialVersionUID = 2L;
	private static Logger logger = Logger.getLogger(BalanceSheetImpl.class.getName());
	protected static long balanceSheetIdSeq = 1;
	
	protected ArrayList<MoneyPool> pools = new ArrayList<MoneyPool>();
	protected String name;
	protected Signature signature;

	protected BalanceSheetImpl() {
		super();
		name = "Laskelma " + balanceSheetIdSeq;
		balanceSheetIdSeq++;
		logger.log(Level.FINE, "New balance sheet created: {0}", this);
	}
	
	@Override 
	public List<MoneyPool> getMoneyPools() {
		return Collections.unmodifiableList(pools);
	}
	
	@Override
	public List<SharedCost> getSharedCosts() {
		ArrayList<SharedCost> sharedCosts = new ArrayList<SharedCost>();
		for (MoneyPool pool : pools) {
			if (pool instanceof SharedCost) {
				sharedCosts.add((SharedCost)pool);
			}
		}
		return Collections.unmodifiableList(sharedCosts);
	}

	@Override
	public List<Person> getPersons() {
		ArrayList<Person> persons = new ArrayList<Person>();
		for (MoneyPool pool : pools) {
			if (pool instanceof Person) {
				persons.add((Person)pool);
			}
		}
		return Collections.unmodifiableList(persons);
	}



	@Override
	public Signature getSignature() {
		return this.signature;
	}

	@Override
	public boolean removeMoneyPool(MoneyPool pool) {
		boolean removed = this.pools.remove(pool);
		if (removed) {
			fireEntityUpdateEvent();
			logger.log(Level.FINE, "Removed pool {0} from {1}", new Object[]{pool, this});
		}
		return removed;
	}

	@Override
	public boolean addMoneyPool(MoneyPool pool) {
		boolean added = this.pools.add(pool);
		if (added) {
			fireEntityUpdateEvent();
			logger.log(Level.FINE, "Added pool {0} to {1}", new Object[]{pool, this});
		}
		return added;
	}

	@Override
	public String toString() {
		return getName();
	}

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public void setName(String name) {
		if (name == null) {
			throw new IllegalArgumentException("Null name not allowed");
		}
		this.name = name;	
		fireEntityUpdateEvent();
		logger.log(Level.FINE, "Name of balance sheet now {0}", name);
	}

	@Override
	public Signature signAsSettled() {
		signature = EntityManager.createSignature();
		fireEntityUpdateEvent();
		logger.log(Level.INFO, "Balance sheet {0} signed as settled by {1}", new Object[]{this, signature.getCreatedBy()});
		return signature;
	}

	@Override
	public List<Debt> getDebts() {
		List<Debt> debts = new ArrayList<Debt>();
		for (Person person : getPersons()) {
			Set<Debt> personDebts = person.getDebts();
			for (Debt debt : personDebts) {
				if (!debts.contains(debt)) {
					debts.add(debt);
				}
			}
		}
		return Collections.unmodifiableList(debts);
	}

	@Override
	public void removeSignature() {
		signature = null;
		fireEntityUpdateEvent();
		logger.log(Level.INFO, "Signature removed from balance sheet {0} by {1}", new Object[]{this, this.getUpdatedBy()});
	}

	@Override
	public void swapMoneyPools(int i, int j) {
		Collections.swap(pools, i, j);
		logger.log(Level.FINE, "Money pools {0} and {1} swapped", new Object[]{i, j});
	}

	@Override
	public void swapPersons(int i, int j) {
		List<Person> persons = getPersons();
		Person person1 = persons.get(i);
		Person person2 = persons.get(j);
		int p = pools.indexOf(person1);
		int q = pools.indexOf(person2);
		swapMoneyPools(p, q);
	}

	@Override
	public void swapSharedCosts(int i, int j) {
		List<SharedCost> costs = getSharedCosts();
		SharedCost cost1 = costs.get(i);
		SharedCost cost2 = costs.get(j);
		int p = pools.indexOf(cost1);
		int q = pools.indexOf(cost2);
		swapMoneyPools(p, q);
	}

}
